package com.kingstar.ceres.data.util;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class SnowIdUtils implements EnvironmentAware {

    @Value("${spring.cloud.client.ip-address:}")
    private String ipAddress;

    static long CONFIG_WORK_ID=1021L;

    @Override
    public void setEnvironment(Environment environment) {
        final String workId = environment.getProperty("ksdata.workId.snow");
        if(StringUtils.isNotBlank(workId)){
            CONFIG_WORK_ID=Long.parseLong(workId);
        }else if(StringUtils.isNotBlank(ipAddress)){
            ipAddress = ipAddress.replaceAll("\\.", "");
            ipAddress = ipAddress.substring(ipAddress.length() - 3);
            CONFIG_WORK_ID=Long.parseLong(ipAddress);
        }else{
            CONFIG_WORK_ID=Math.round(110+Math.random()*900);
        }
        SnowIdUtils.uniqueLong();
    }

    /**
     * 私有的 静态内部类
     */

    private static class SnowFlake {
        private static final SnowFlake SNOW_FLAKE = new SnowFlake();
        //因为二进制里第一个 bit 为如果是 1,那么都是负数,但是我们生成的 id 都是正数,所以第一个 bit 统一都是 0。

        //机器ID  2进制10位  32位减掉1位 31个
        private long workerId;
        //机房ID 2进制5位  32位减掉1位 31个
//    private long datacenterId;
        //代表一毫秒内生成的多个id的最新序号  12位 4096 -1 = 4095 个
        private long sequence;
        //设置一个时间初始值    2^41 - 1   差不多可以用69年
        private long twepoch = 1585644268888L;
        //10位的机器id
        private long workerIdBits = 10L;
        //5位的机房id
//    private long datacenterIdBits = 5L;
        //每毫秒内产生的id数 2 的 12次方
        private long sequenceBits = 12L;
        // 这个是二进制运算,就是5 bit最多只能有31个数字,也就是说机器id最多只能是32以内
        private long maxWorkerId = -1L ^ (-1L << workerIdBits);
        // 这个是一个意思,就是5 bit最多只能有31个数字,机房id最多只能是32以内
//    private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);

        private long workerIdShift = sequenceBits;
        private long datacenterIdShift = sequenceBits + workerIdBits;
        //    private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
        private long sequenceMask = -1L ^ (-1L << sequenceBits);
        //记录产生时间毫秒数,判断是否是同1毫秒
        private long lastTimestamp = -1L;
        private long getWorkerId(){
            return workerId;
        }

        private long getTimestamp() {
            return SystemClock.now();
        }


        private SnowFlake() {
            long workId=CONFIG_WORK_ID;
            log.info("snow workId: {}",workId);
            init(workId,0L);
        }

        private SnowFlake(long workerId, long sequence) {
            init(workerId,sequence);
        }

        private void init(long workerId, long sequence){
            // 检查机房id和机器id是否超过31 不能小于0
            if (workerId > maxWorkerId || workerId < 0) {
                throw new IllegalArgumentException(
                        String.format("worker Id can&#39;t be greater than %d or less than 0",maxWorkerId));
            }

            this.workerId = workerId;
            this.sequence = sequence;
        }

        // 这个是核心方法,通过调用nextId()方法,让当前这台机器上的snowflake算法程序生成一个全局唯一的id
        private synchronized long nextId() {
            // 这儿就是获取当前时间戳,单位是毫秒
            long timestamp = timeGen();
            if (timestamp < lastTimestamp) {

                System.err.printf(
                        "clock is moving backwards. Rejecting requests until %d.", lastTimestamp);
                throw new RuntimeException(
                        String.format("Clock moved backwards. Refusing to generate id for %d milliseconds",
                                lastTimestamp - timestamp));
            }

            // 下面是说假设在同一个毫秒内,又发送了一个请求生成一个id
            // 这个时候就得把seqence序号给递增1,最多就是4096
            if (lastTimestamp == timestamp) {

                // 这个意思是说一个毫秒内最多只能有4096个数字,无论你传递多少进来,
                //这个位运算保证始终就是在4096这个范围内,避免你自己传递个sequence超过了4096这个范围
                sequence = (sequence + 1) & sequenceMask;
                //当某一毫秒的时间,产生的id数 超过4095,系统会进入等待,直到下一毫秒,系统继续产生ID
                if (sequence == 0) {
                    timestamp = tilNextMillis(lastTimestamp);
                }

            } else {
                sequence = 0;
            }
            // 这儿记录一下最近一次生成id的时间戳,单位是毫秒
            lastTimestamp = timestamp;
            // 这儿就是最核心的二进制位运算操作,生成一个64bit的id
            // 先将当前时间戳左移,放到41 bit那儿&#xff1b;将机房id左移放到5 bit那儿&#xff1b;将机器id左移放到5 bit那儿&#xff1b;将序号放最后12 bit
            // 最后拼接起来成一个64 bit的二进制数字,转换成10进制就是个long型
            return ((timestamp - twepoch) << datacenterIdShift) |
                    (workerId << workerIdShift) | sequence;
        }

        /**
         * 当某一毫秒的时间,产生的id数 超过4095,系统会进入等待,直到下一毫秒,系统继续产生ID
         * @param lastTimestamp
         * @return
         */
        private long tilNextMillis(long lastTimestamp) {

            long timestamp = timeGen();

            while (timestamp <= lastTimestamp) {
                timestamp = timeGen();
            }
            return timestamp;
        }
        //获取当前时间戳
        private long timeGen(){
            return SystemClock.now();
        }

    }

 
    /**
     * 获取long类型雪花ID
     */
    public static long uniqueLong() {
        return SnowFlake.SNOW_FLAKE.nextId();
    }
    /**
     * 获取String类型雪花ID
     */
    public static String uniqueString() {
        return String.valueOf(uniqueLong());
    }

}